﻿using System;
using System.IO.Ports;
using System.Windows.Forms;
using System.Collections.Generic;
using System.Drawing;

namespace IndustrialSerialApp
{
    public partial class MainForm : Form
    {
        private SerialPort _serialPort;
        private readonly Dictionary<string, string> _keyMapping = new Dictionary<string, string>
        {
            {"1", "M1"}, {"2", "M2"}, {"3", "M3"}, {"4", "M4"}, {"5", "M5"},
            {"6", "M6"}, {"7", "M7"}, {"8", "M8"}, {"9", "M9"}, {"0", "M0"},
            {"Menu", "M<"}, {"+", "M>"}, {"OK", "M="}, {".", "M:"}, {"←", "M;"}, {"-", "M?"}
        };

        private bool IsModbusRTU => modbusMode.SelectedItem.ToString() == "RTU";

        public MainForm()
        {
            InitializeComponent();
            LoadAvailablePorts();
            SetupKeyButtons();
            
            // 启用键盘事件
            this.KeyPreview = true;
            this.KeyDown += MainForm_KeyDown;
            
            // 添加PreviewKeyDown事件处理
            this.PreviewKeyDown += MainForm_PreviewKeyDown;
            
            // 为所有可能响应Enter键的按钮添加PreviewKeyDown事件处理
            connectButton.PreviewKeyDown += Button_PreviewKeyDown;
            refreshPortsButton.PreviewKeyDown += Button_PreviewKeyDown;
            sendCommandButton.PreviewKeyDown += Button_PreviewKeyDown;
            
            // 初始化键盘可见性
            UpdateKeyboardVisibility();
            
            // 禁用Enter键作为默认按钮触发
            this.AcceptButton = null;
        }

        private void LoadAvailablePorts()
        {
            try 
            {
                // 清空当前列表
                portComboBox.Items.Clear();
                
                // 获取所有可用的串口名称
                string[] ports = SerialPort.GetPortNames();
                
                // 将串口添加到下拉列表中
                portComboBox.Items.AddRange(ports);
                
                // 如果有可用串口,选择第一个
                if (portComboBox.Items.Count > 0)
                {
                    portComboBox.SelectedIndex = 0;
                }
                else 
                {
                    AppendToLog("未检测到可用的串口");
                }
            }
            catch (Exception ex)
            {
                AppendToLog($"加载串口列表时出错: {ex.Message}");
            }
        }

        private void OnConnectButtonClick(object sender, EventArgs e)
        {
            try
            {
                if (_serialPort == null || !_serialPort.IsOpen)
                {
                    if (portComboBox.SelectedItem == null)
                    {
                        AppendToLog("请先选择一个有效的串口");
                        return;
                    }
                    
                    _serialPort = new SerialPort(portComboBox.SelectedItem.ToString())
                    {
                        BaudRate = int.Parse(baudRateComboBox.SelectedItem.ToString()),
                        Parity = parityComboBox.SelectedIndex == 0 ? Parity.None : 
                                 parityComboBox.SelectedIndex == 1 ? Parity.Odd : Parity.Even,
                        DataBits = int.Parse(dataBitsComboBox.SelectedItem.ToString()),
                        StopBits = stopBitsComboBox.SelectedIndex == 0 ? StopBits.One :
                                   stopBitsComboBox.SelectedIndex == 1 ? StopBits.OnePointFive : StopBits.Two
                    };
                    
                    // 设置错误处理
                    SetupSerialPortErrorHandling();

                    try
                    {
                        _serialPort.Open();
                        
                        // 更新UI状态
                        connectButton.Text = "断开";
                        connectionStatusIndicator.BackColor = Color.Green;
                        AppendToLog($"已连接到串口: {portComboBox.SelectedItem.ToString()}, " +
                                             $"波特率: {baudRateComboBox.SelectedItem}, " +
                                             $"数据位: {dataBitsComboBox.SelectedItem}, " +
                                             $"停止位: {stopBitsComboBox.SelectedItem}, " +
                                             $"校验位: {parityComboBox.SelectedItem}");
                        
                        // 禁用设置控件
                        portComboBox.Enabled = false;
                        baudRateComboBox.Enabled = false;
                        dataBitsComboBox.Enabled = false;
                        stopBitsComboBox.Enabled = false;
                        parityComboBox.Enabled = false;
                        refreshPortsButton.Enabled = false;
                    }
                    catch (UnauthorizedAccessException)
                    {
                        AppendToLog("无法访问串口，可能已被其他程序占用");
                    }
                    catch (System.IO.IOException)
                    {
                        AppendToLog("串口不存在或无法打开");
                    }
                    catch (Exception ex)
                    {
                        AppendToLog($"打开串口时出错: {ex.Message}");
                    }
                }
                else
                {
                    try
                    {
                        _serialPort.Close();
                        
                        // 更新UI状态
                        HandleDisconnection();
                    }
                    catch (Exception ex)
                    {
                        AppendToLog($"关闭串口时出错: {ex.Message}");
                        // 尝试强制更新UI状态
                        HandleDisconnection();
                    }
                }
            }
            catch (Exception ex)
            {
                connectionStatusIndicator.BackColor = Color.Red;
                AppendToLog($"连接操作出错: {ex.Message}");
            }
        }

        private void OnSendCommandButtonClick(object sender, EventArgs e)
        {
            try
            {
                if (_serialPort != null && _serialPort.IsOpen)
                {
                    string command = GenerateModbusCommand();
                    
                    // 确保命令末尾有CR+LF
                    if (!command.EndsWith("\r\n"))
                    {
                        command += "\r\n";
                    }
                    
                    _serialPort.Write(command); // 使用Write而不是WriteLine
                    AppendToLog($"发送: {command}");
                }
                else
                {
                    AppendToLog("串口未打开");
                }
            }
            catch (Exception ex)
            {
                AppendToLog($"发送命令时出错: {ex.Message}");
            }
        }

        private string GenerateModbusCommand()
        {
            string address = addressTextBox.Text;
            string function = functionCodeTextBox.SelectedItem?.ToString();
            string register = registerTextBox.Text;
            string count = countTextBox.Text;

            string frame = $"{address}{function}{register}{count}";
            string checksum;
            
            if (IsModbusRTU)
            {
                checksum = ComputeCRC(frame); // RTU模式使用CRC
                return frame + checksum;
            }
            else
            {
                // ASCII模式，增加冒号前缀，使用LRC
                checksum = ComputeLRC(frame); 
                return ":" + frame + checksum;
            }
        }

        private string ComputeCRC(string data)
        {
            byte[] dataBytes = StringToByteArray(data);
            ushort crc = 0xFFFF;

            foreach (byte byteData in dataBytes)
            {
                crc ^= byteData;
                for (int i = 0; i < 8; i++)
                {
                    if ((crc & 0x0001) != 0)
                    {
                        crc >>= 1;
                        crc ^= 0xA001;
                    }
                    else
                    {
                        crc >>= 1;
                    }
                }
            }

            // 交换字节顺序（低字节在前，高字节在后）
            byte lowByte = (byte)(crc & 0xFF);
            byte highByte = (byte)(crc >> 8);
            
            return $"{lowByte:X2}{highByte:X2}";
        }

        private string ComputeLRC(string data)
        {
            byte[] bytes = StringToByteArray(data);
            byte lrc = 0;
            
            foreach (byte b in bytes)
            {
                lrc += b;
            }
            
            lrc = (byte)(-lrc);
            return lrc.ToString("X2");
        }

        private byte[] StringToByteArray(string hex)
        {
            int length = hex.Length / 2;
            byte[] bytes = new byte[length];
            for (int i = 0; i < length; i++)
            {
                bytes[i] = Convert.ToByte(hex.Substring(i * 2, 2), 16);
            }
            return bytes;
        }

        private void OnRefreshPortsButtonClick(object sender, EventArgs e)
        {
            LoadAvailablePorts();
        }

        private void SetupKeyButtons()
        {
            // 为数字键添加事件处理
            key0Button.Click += (s, e) => SendKeyCommand("0");
            key1Button.Click += (s, e) => SendKeyCommand("1");
            key2Button.Click += (s, e) => SendKeyCommand("2");
            key3Button.Click += (s, e) => SendKeyCommand("3");
            key4Button.Click += (s, e) => SendKeyCommand("4");
            key5Button.Click += (s, e) => SendKeyCommand("5");
            key6Button.Click += (s, e) => SendKeyCommand("6");
            key7Button.Click += (s, e) => SendKeyCommand("7");
            key8Button.Click += (s, e) => SendKeyCommand("8");
            key9Button.Click += (s, e) => SendKeyCommand("9");
            
            // 为功能键添加事件处理
            keyM1Button.Click += (s, e) => SendKeyCommand("Menu");
            keyM2Button.Click += (s, e) => SendKeyCommand("+");
            keyM3Button.Click += (s, e) => SendKeyCommand("OK");
            keyM4Button.Click += (s, e) => SendKeyCommand(".");
            keyM5Button.Click += (s, e) => SendKeyCommand("←");
            keyM6Button.Click += (s, e) => SendKeyCommand("-");
        }

        private void SendKeyCommand(string key)
        {
            try
            {
                if (_serialPort != null && _serialPort.IsOpen && _keyMapping.ContainsKey(key))
                {
                    string command = _keyMapping[key] + "\r\n";
                    _serialPort.Write(command);
                    AppendToLog($"已发送: {command}");
                }
                else
                {
                    AppendToLog("串口未打开或按键未映射");
                }
            }
            catch (Exception ex)
            {
                AppendToLog($"发送命令时出错: {ex.Message}");
            }
        }

        private void MainForm_KeyDown(object sender, KeyEventArgs e)
        {
            // 检查是否按下了Enter键
            if (e.KeyCode == Keys.Enter)
            {
                // 发送相应命令
                SendKeyCommand("OK");
                
                // 标记为已处理
                e.Handled = true;
                e.SuppressKeyPress = true; // 这会阻止键盘事件进一步传播
                
                // 阻止默认行为
                return;
            }
            
            // 处理其他键
            switch (e.KeyCode)
            {
                case Keys.D0:
                case Keys.NumPad0:
                    SendKeyCommand("0");
                    e.Handled = true;
                    e.SuppressKeyPress = true;
                    break;
                case Keys.D1:
                case Keys.NumPad1:
                    SendKeyCommand("1");
                    e.Handled = true;
                    e.SuppressKeyPress = true;
                    break;
                case Keys.D2:
                case Keys.NumPad2:
                    SendKeyCommand("2");
                    e.Handled = true;
                    e.SuppressKeyPress = true;
                    break;
                case Keys.D3:
                case Keys.NumPad3:
                    SendKeyCommand("3");
                    e.Handled = true;
                    e.SuppressKeyPress = true;
                    break;
                case Keys.D4:
                case Keys.NumPad4:
                    SendKeyCommand("4");
                    e.Handled = true;
                    e.SuppressKeyPress = true;
                    break;
                case Keys.D5:
                case Keys.NumPad5:
                    SendKeyCommand("5");
                    e.Handled = true;
                    e.SuppressKeyPress = true;
                    break;
                case Keys.D6:
                case Keys.NumPad6:
                    SendKeyCommand("6");
                    e.Handled = true;
                    e.SuppressKeyPress = true;
                    break;
                case Keys.D7:
                case Keys.NumPad7:
                    SendKeyCommand("7");
                    e.Handled = true;
                    e.SuppressKeyPress = true;
                    break;
                case Keys.D8:
                case Keys.NumPad8:
                    SendKeyCommand("8");
                    e.Handled = true;
                    e.SuppressKeyPress = true;
                    break;
                case Keys.D9:
                case Keys.NumPad9:
                    SendKeyCommand("9");
                    e.Handled = true;
                    e.SuppressKeyPress = true;
                    break;
                case Keys.M:
                    SendKeyCommand("Menu");
                    e.Handled = true;
                    e.SuppressKeyPress = true;
                    break;
                case Keys.Add:
                case Keys.Oemplus:
                    SendKeyCommand("+");
                    e.Handled = true;
                    e.SuppressKeyPress = true;
                    break;
                case Keys.OemPeriod:
                case Keys.Decimal:
                    SendKeyCommand(".");
                    e.Handled = true;
                    e.SuppressKeyPress = true;
                    break;
                case Keys.Back:
                    SendKeyCommand("←");
                    e.Handled = true;
                    e.SuppressKeyPress = true;
                    break;
                case Keys.OemMinus:
                case Keys.Subtract:
                    SendKeyCommand("-");
                    e.Handled = true;
                    e.SuppressKeyPress = true;
                    break;
            }
        }

        private void MainForm_PreviewKeyDown(object sender, PreviewKeyDownEventArgs e)
        {
            // 如果按下的是Enter键，标记为需要处理
            if (e.KeyCode == Keys.Enter)
            {
                e.IsInputKey = true; // 这会让窗体保留对Enter键的控制，而不是让其它控件处理
            }
        }

        private void Button_PreviewKeyDown(object sender, PreviewKeyDownEventArgs e)
        {
            // 如果按下的是Enter键，标记为需要处理
            if (e.KeyCode == Keys.Enter)
            {
                e.IsInputKey = true; // 这会让窗体保留对Enter键的控制，而不是让按钮处理
            }
        }

        private void UpdateChecksum()
        {
            try
            {
                string address = addressTextBox.Text;
                string function = functionCodeTextBox.SelectedItem?.ToString();
                string register = registerTextBox.Text;
                string count = countTextBox.Text;

                if (string.IsNullOrEmpty(address) || string.IsNullOrEmpty(function) || 
                    string.IsNullOrEmpty(register) || string.IsNullOrEmpty(count))
                {
                    checksumTextBox.Text = "";
                    return;
                }

                string frame = $"{address}{function}{register}{count}";
                string checksum = IsModbusRTU ? ComputeCRC(frame) : ComputeLRC(frame);
                
                // 对于ASCII模式，显示带冒号的完整命令
                if (!IsModbusRTU)
                {
                    checksumTextBox.Text = checksum + " (:" + frame + checksum + ")";
                }
                else
                {
                    checksumTextBox.Text = checksum;
                }
            }
            catch
            {
                checksumTextBox.Text = "无效数据";
            }
        }

        // 修改虚拟键盘显示/隐藏方法
        private void UpdateKeyboardVisibility()
        {
            keyboardContainer.Visible = enableKeyboardCheckBox.Checked;
            
            // 强制重新布局
            this.PerformLayout();
            this.Refresh();
            
            // 记录到日志，便于调试
            AppendToLog($"虚拟键盘已{(enableKeyboardCheckBox.Checked ? "显示" : "隐藏")}");
        }

        // 添加错误处理和异常处理方法
        private void SetupSerialPortErrorHandling()
        {
            if (_serialPort != null)
            {
                // 设置数据接收事件
                _serialPort.DataReceived += OnDataReceived;
                
                // 设置错误接收事件
                _serialPort.ErrorReceived += OnErrorReceived;
                
                // 设置PinChanged事件（可检测物理连接状态变化）
                _serialPort.PinChanged += OnPinChanged;
            }
        }

        private void OnDataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            try
            {
                if (_serialPort != null && _serialPort.IsOpen)
                {
                    // 使用 ReadExisting 而不是 ReadLine 以避免阻塞
                    string data = _serialPort.ReadExisting();
                    
                    // 使用Invoke在UI线程更新控件
                    this.Invoke((Action)(() =>
                    {
                        // 确保每个日志条目都有换行符
                        if (!data.EndsWith("\n"))
                        {
                            data += "\n";
                        }
                        AppendToLog($"接收: {data}");
                    }));
                }
            }
            catch (Exception ex)
            {
                this.Invoke((Action)(() =>
                {
                    AppendToLog($"接收数据时出错: {ex.Message}");
                }));
            }
        }

        private void OnErrorReceived(object sender, SerialErrorReceivedEventArgs e)
        {
            this.Invoke((Action)(() =>
            {
                AppendToLog($"串口错误: {e.EventType}");
                
                if (_serialPort != null && _serialPort.IsOpen)
                {
                    try
                    {
                        _serialPort.Close();
                    }
                    catch { /* 忽略关闭时的错误 */ }
                    
                    HandleDisconnection();
                }
            }));
        }

        private void OnPinChanged(object sender, SerialPinChangedEventArgs e)
        {
            // 检测物理连接断开
            if (e.EventType == SerialPinChange.DsrChanged || 
                e.EventType == SerialPinChange.CtsChanged)
            {
                this.Invoke((Action)(() =>
                {
                    // 检查是否为断开连接
                    if (_serialPort != null && !_serialPort.DsrHolding && !_serialPort.CtsHolding)
                    {
                        AppendToLog("检测到物理连接已断开");
                        
                        if (_serialPort.IsOpen)
                        {
                            try
                            {
                                _serialPort.Close();
                            }
                            catch { /* 忽略关闭时的错误 */ }
                        }
                        
                        HandleDisconnection();
                    }
                }));
            }
        }

        private void HandleDisconnection()
        {
            // 更新UI状态
            connectButton.Text = "连接";
            connectionStatusIndicator.BackColor = Color.Red;
            AppendToLog("连接已断开");
            
            // 启用设置控件
            portComboBox.Enabled = true;
            baudRateComboBox.Enabled = true;
            dataBitsComboBox.Enabled = true;
            stopBitsComboBox.Enabled = true;
            parityComboBox.Enabled = true;
            refreshPortsButton.Enabled = true;
        }

        // 确保所有日志条目都有换行符
        private void AppendToLog(string message)
        {
            if (!message.EndsWith("\n"))
            {
                message += "\n";
            }
            logTextBox.AppendText(message);
            
            // 滚动到最后一行
            logTextBox.SelectionStart = logTextBox.Text.Length;
            logTextBox.ScrollToCaret();
        }
    }
}
